#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

#define SERVER_PORT 5432
#define MAX_PENDING 5
#define MAX_LINE 256

void *client_recv(void *arg) {
    int client_sock = *((int *)arg);
    char buf[MAX_LINE];
    int len;

    while ((len = recv(client_sock, buf, sizeof(buf), 0)) > 0) {
        buf[len] = '\0';
        printf("Client: %s\n", buf);
    }

    if (len < 0) {
        perror("recv");
    }

    close(client_sock);
    free(arg);  
    return NULL;
}

void *client_send(void *arg) {
    int client_sock = *((int *)arg);
    char buf[MAX_LINE];

    while (fgets(buf, sizeof(buf), stdin)) {
        buf[strcspn(buf, "\n")] = 0; 
        send(client_sock, buf, strlen(buf), 0);
    }

    close(client_sock);
    free(arg); 
    return NULL;
}

void *handle_client(void *arg) {
    int client_sock = *((int *)arg);
    int *client_sock_ptr = malloc(sizeof(int));  
    if (client_sock_ptr == NULL) {
        perror("Failed to allocate memory for client_sock_ptr");
        close(client_sock);
        free(arg);
        return NULL;
    }
    *client_sock_ptr = client_sock;

    pthread_t receive_tid, send_tid;

    if (pthread_create(&receive_tid, NULL, client_recv, client_sock_ptr) != 0) {
        perror("Failed to create receive thread");
        close(client_sock);
        free(arg);
        return NULL;
    }

    if (pthread_create(&send_tid, NULL, client_send, client_sock_ptr) != 0) {
        perror("Failed to create send thread");
        close(client_sock);
        free(arg);
        return NULL;
    }

    pthread_join(receive_tid, NULL);
    pthread_join(send_tid, NULL);

    return NULL;
}

int main() {
    int server_sock, client_sock;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    pthread_t client_handler_tid;

    server_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (server_sock < 0) {
        perror("Error: socket");
        exit(1);
    }

    bzero((char *)&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(SERVER_PORT);

    if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Error: bind");
        exit(1);
    }

    listen(server_sock, MAX_PENDING);

    while (1) {
        client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_sock < 0) {
            perror("Error: accept");
            continue;
        }

        int *client_sock_ptr = malloc(sizeof(int));  // Allocate memory for client_sock
        if (client_sock_ptr == NULL) {
            perror("Failed to allocate memory for client_sock_ptr");
            close(client_sock);
            continue;
        }
        *client_sock_ptr = client_sock;

        pthread_create(&client_handler_tid, NULL, handle_client, (void *)client_sock_ptr);
        pthread_detach(client_handler_tid);
    }

    close(server_sock);
    return 0;
}
